package Image;

import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.util.LinkedList;
import java.util.ListIterator;
import javax.media.jai.ROI;
import javax.media.jai.StatisticsOpImage;

/**
 * This class was originally intended to only calculate the Centroid of an ROI but
 * now calculates the smallest rectangle around the ROI and the area of the ROI.
 *
 * We would have liked to implement Texture, however, {@link #accumulateStatistics(java.lang.String, java.awt.image.Raster, java.lang.Object) }
 * returns the rectangles only within the ROI and since the GLCM window needs to be
 * calcuated just outside the boundary (as it's rectangular and ingeneral the ROI is not) 
 * we were unsure how to implement it within this class and didn't have time to determine
 * how to do so.
 */
public class CentroidOpImage extends StatisticsOpImage {
	/**
	 * The mean x value
	 */
	private double xMean = 0.0;
	/**
	 * The sum of all means along the x axis
	 */
	private double xMeanSum = 0.0;
	/**
	 * The mean y value
	 */
	private double yMean = 0.0;
	/**
	 * The sum of all means along the y axis
	 */
	private double yMeanSum = 0.0;
	/**
	 * The centroid of the Image
	 */
	private Point p = new Point((int)xMean,(int)yMean);
	/**
	 * The total number of rectangles in the ROI
	 */
	private int rectCount = 0;
	/**
	 * The area smallest rectangle surrounding the segment
	 */
	private Rectangle rectangle;
	/**
	 * The area of the segment
	 */
	private int segmentArea = 0;
	/*
	 * The perimeter of the segment
	 */
//	private int perimeter = 0;
        
        /**
	 * The constructor for the class, which will call the superclass constructor,
	 * store some of the parameters' values locally and initialize other fields.
	 * @param source the source image
	 * @param color the RGBColor used for comparisons
	 */
	public CentroidOpImage(
			RenderedImage source, ROI roi, int xStart, int yStart, int xPeriod, int yPeriod) {
		super(source, roi, xStart, yStart, xPeriod, yPeriod);
	}

	/**
	 * This method will be called when we want to get a specific property from
	 * the resulting image.
	 * @param name the property name
	 */
	@Override
	public Object getProperty(String name) {
		super.getProperty(name);

		// super.getProperty will calculate the centroid. We call it only once.
		if (name.equalsIgnoreCase("centroid")) {
			return p;
		} else if (name.equalsIgnoreCase("xMean")) {
			return xMean;
		} else if (name.equalsIgnoreCase("yMean")) {
			return yMean;
		} else if (name.equalsIgnoreCase("area")) {
			return segmentArea;
		} else if (name.equalsIgnoreCase("rectangle")) {
			return rectangle;
//		} else if (name.equalsIgnoreCase("perimeter")) {
//			return perimeter;
		} else {
			return Image.UndefinedProperty;
		}
	}

	/**
	 * This method returns an array of the known statistics (properties) names.
	 */
	protected String[] getStatisticsNames() {
		return new String[]{
			"centroid", "xMean", "yMean",
//			"perimeter",
			"area","rectangle"
		};
	}

	/**
	 * This method will be invoked to create an object to store a statistic.
	 * In this case we simply ignore the name parameter, since all statistics
	 * will be always calculated.
	 */
	protected Object createStatistics(String name) {
		Object stat = java.awt.Image.UndefinedProperty;
		if (name.equalsIgnoreCase("centroid")) {
			p = new Point((int)xMean, (int) yMean);
			stat = p;
		} else if (name.equalsIgnoreCase("xmean")) {
			stat = new Double(xMean);
		} else if (name.equalsIgnoreCase("ymean")) {
			stat = new Double(yMean);
		} else if (name.equalsIgnoreCase("rectangleArea")) {
			stat = new Rectangle(rectangle);
		} else if (name.equalsIgnoreCase("segmentArea")) {
			stat = new Integer(segmentArea);
//		} else if (name.equalsIgnoreCase("perimeter")) {
//			stat = new Integer(perimeter);
		}
		
		return stat;
	}

	private final int startPosition(int pos, int start, int period) {
            
		int t = (pos - start) % period;
		if (t == 0) {
			return pos;
		} else {
			return (pos + (period - t));
		}
	}

	/**
	 * This method will be called for each tile on the image.
	 * In this example, since we use global variables for the statistics, the
	 * parameters name and stats are ignored.
	 */
	protected void accumulateStatistics(String name, Raster source, Object stats) {
		int xMin = Integer.MAX_VALUE,
			xMax = Integer.MIN_VALUE,
			yMin = Integer.MAX_VALUE,
			yMax = Integer.MIN_VALUE;
	
		Rectangle srcBounds = getSourceImage(0).getBounds()
			.intersection(source.getBounds());

		if (p == null) p = new Point((int)xMean, (int)yMean);

		// Get the rectangles for the ROI or the entire image if the ROI is not specified
		LinkedList rectList;
		if (roi == null) { // ROI is the whole Raster
			rectList = new LinkedList();
			rectList.addLast(srcBounds);
		} else {
			rectList = roi.getAsRectangleList(
				srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height
			);
			if (rectList == null) {
				return; // ROI does not intersect with Raster boundary.
			}
		}
		ListIterator iterator = rectList.listIterator(0);

		// Process each rectangle
		while (iterator.hasNext()) {
			Rectangle rect = srcBounds.intersection((Rectangle) iterator.next());

			int tx = rect.x;
			int ty = rect.y;

			/* Find the actual ROI based on start and period. */
			rect.x = startPosition(tx, xStart, xPeriod);
			rect.y = startPosition(ty, yStart, yPeriod);
			rect.width = tx + rect.width - rect.x;
			rect.height = ty + rect.height - rect.y;

			if (rect.isEmpty()) continue; // no pixel to count in this rectangle

			// Find the minimum/maximum x and y for smallest rectangle around the ROI
			if (rect.x < xMin) xMin = rect.x;
			if (rect.x > xMax) xMax = rect.x;
			if (rect.y < yMin) yMin = rect.y;
			if (rect.y > yMax) yMax = rect.y;

			// Sum for calculating the center of the ROI
			xMeanSum += (double)rect.width/2.0 + (double)rect.x;
			yMeanSum += (double)rect.height/2.0 + (double)rect.y;

			// Sum for calcuating the area, all rectangles are 1px tall so height is not
			// necessary
			segmentArea += rect.width;

			// Used to calculate the center of the ROI
			rectCount++;
		}

		// Divide to get the center of the ROI
		xMean = xMeanSum/(double)rectCount;
		yMean = yMeanSum/(double)rectCount;

		// Create an object for the smallest rectangle around the ROI
		rectangle = new Rectangle(xMin, yMin, (xMax - xMin), (yMax - yMin));

		// Create an object for the center of the ROI
		p = new Point((int)xMean, (int)yMean);
	}
}